// -----------------------------
// Author: 		Dhaenens Gert	
// Creation: 	1/3/2015 12:18:27 PM				
// -----------------------------

#include "SongGenerator.h"

#include "Application.h"
#include "AudioManager.h"

#include "Sound.h"

#include "SoundSampler.h"

#include "SoundPlugin.h"
#include "TripleOscillator.h"

#include "SoundFilter.h"
#include "LFOFilter.h"

#include "SoundPass.h"
#include "VolumeEnvelope.h"
#include "LowPass.h"
#include "HighPass.h"
#include "DelayPass.h"
#include "FadePass.h"
#include "ReverbPass.h"

#define SAMPLERATE		44100
#define NOTELENGTH		0.2f

Sound* SongGenerator::Generate(Application* lpApp, const SongDesc& desc)
{

	// Generate the Elements
	SoundSample elements[] = 
	{
		//GenerateOverlay(lpApp, desc),
		GenerateMelody(lpApp, desc),
		GenerateMelody(lpApp, desc),
	};

	// Merge the Elements
	SoundSample sample = {};
	sample.Channels = 2;
	sample.SampleRate = SAMPLERATE;
	for (const SoundSample& element : elements)
		sample.Merge(element);

	// Create the Sound
	Sound* lpSound = lpApp->GetAudio()->CreateSoundFromData(lpApp, sample.Samples, sample.Count, sample.Channels);

	return lpSound;
}

SoundSample SongGenerator::GenerateMelody(Application* lpApp, const SongDesc& desc)
{
	SoundSampler sampler(SAMPLERATE);
	
	// Triple Oscillator Plugin
	TripleOscillator* lpPlugin = new TripleOscillator(desc.Melody.Type, desc.Melody.Type, desc.Melody.Type);
	lpPlugin->Osc1Coarse = -20;
	lpPlugin->Osc2Coarse = 17;
	lpPlugin->Osc2RelativeVolume = 0.9f;
	lpPlugin->Osc2PhaseOffset = 0.0f;
	lpPlugin->Osc3Coarse = 0;
	lpPlugin->Osc3RelativeVolume = 0.6f;
	lpPlugin->Osc3PhaseOffset = 0.000f;
	sampler.SetPlugin(lpPlugin);

	// Volume Envelope Pass
	VolumeEnvelope* lpVolEnvPass = new VolumeEnvelope();
	lpVolEnvPass->AttackTime = 0.4f;
	lpVolEnvPass->AttackValue = 1.0f;
	lpVolEnvPass->DecayTime = 0.0f;
	lpVolEnvPass->SustainValue = 1.0f;
	lpVolEnvPass->ReleaseTime = 0.4f;
	sampler.AddPass(lpVolEnvPass);

	// Low Pass
	LowPass* lpLowPass = new LowPass();
	lpLowPass->CutoffFrequency = 700.0f;
	lpLowPass->QualityFactor = 1.0f / Math::Sqrt(2.0f);
	lpLowPass->Gain = 1.0f;
	lpLowPass->Modulate = true;
	lpLowPass->ModulateType = SoundWaveType::Sine;
	lpLowPass->ModulateFrequency = 1.0f;
	lpLowPass->ModulateAmplitude = 0.8f;
	lpLowPass->ModulateStrength = 500;
	sampler.AddPass(lpLowPass);

	// Reverb Pass
	ReverbPass* lpReverbPass = new ReverbPass();
	lpReverbPass->PreDelay = 0.03f;
	lpReverbPass->Decay = desc.Melody.MinimumNoteLength * 1.2f;
	lpReverbPass->LowCut = 60.0f;
	lpReverbPass->HighCut = desc.Melody.BaseFrequency * 1.2f;
	lpReverbPass->DryLevel = 1.0f;
	lpReverbPass->WetLevel = 0.5f;
	sampler.AddPass(lpReverbPass);

	SoundSample sample = {};
	sample.Channels = 2;
	sample.SampleRate = SAMPLERATE;

	UInt32 notes = Math::Random(desc.Melody.MinimumNotes, desc.Melody.MaximumNotes);
	UInt32 harmonics = 20;
	UInt32 mergeSampleOffset = 0;
	for (UInt32 i = 0; i < notes; ++i)
	{
		Int32 noteOffset = Math::Random(desc.Melody.MinimumNoteOffset, desc.Melody.MaximumNoteOffset);
		Float fScale = SoundSample::CentsToFreqScale(noteOffset * 200);
		Float f = desc.Melody.BaseFrequency * fScale;
		Float d = Math::Random(desc.Melody.MinimumNoteLength, desc.Melody.MaximumNoteLength) * NOTELENGTH;

		SoundSample newSample1 = sampler.Generate(f, desc.Melody.Amplitude, harmonics, d);
		SoundSample newSample2 = sampler.Generate(f * SoundSample::CentsToFreqScale(-400), desc.Melody.Amplitude, harmonics, d);
		SoundSample newSample3 = sampler.Generate(f * SoundSample::CentsToFreqScale(400), desc.Melody.Amplitude, harmonics, d);

		SoundSample newSample = {};
		newSample.Channels = 2;
		newSample.SampleRate = SAMPLERATE;
		newSample.Merge(newSample1);
		newSample.Merge(newSample2);
		newSample.Merge(newSample3);

		Float overlap = Math::Random(desc.Melody.MinimumOverlap, desc.Melody.MaximumOverlap) * NOTELENGTH;
		UInt32 offset = (UInt32)((overlap * SAMPLERATE) * 2);
		mergeSampleOffset += offset;
		sample.Merge(newSample, mergeSampleOffset);
	}

	return sample;
}